//
//  WindowSniffer.m
//  TruncationDetector
//
//  Created by taodongl on 8/18/15.
//  Copyright (c) 2015 taodongl. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "GRMustache/GRMustache.h"
#import "WindowSniffer.h"
@interface WindowSniffer()

@end

@interface TruncationData : NSObject
@property (nonatomic, assign, readwrite) float frameX;
@property (nonatomic, assign, readwrite) float frameY;
@property (nonatomic, assign, readwrite) float frameWidth;
@property (nonatomic, assign, readwrite) float frameHeight;
@property (nonatomic, copy, readwrite) NSString * img;
@property (nonatomic, copy, readwrite) NSString * content;
@property (nonatomic, copy, readwrite) NSString * className;
@end

@implementation TruncationData
@synthesize img = _img;
@synthesize content = _content;
@synthesize className = _className;
- (id) init: (CGRect) rect
{
    self.frameX = rect.origin.x;
    self.frameY = rect.origin.y;
    self.frameWidth = rect.size.width;
    self.frameHeight = rect.size.height;
    return self;
}
@end
@implementation WindowSniffer
@synthesize snifferData = _snifferData;

- (id) init
{
    NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
    NSTimeZone* gmt = [NSTimeZone timeZoneWithAbbreviation: @"GMT"];
    [dateFormatter setTimeZone: gmt];
    dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSSSSS zzz";
    _snifferData = [NSMutableDictionary new];
    _snifferData[@"date"] = [dateFormatter stringFromDate: [NSDate date]];
    mainWindow = [WindowSniffer getMainWindow];
    [self walkAllViews];
    return self;
}


- (void) walkAllViews
{
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
    self.snifferData[@"appName"] = appName;
    UIWindow *screenWindow = [[UIApplication sharedApplication] keyWindow];
    self.snifferData[@"width"] = @(mainWindow.frame.size.width);
    self.snifferData[@"height"] = @(mainWindow.frame.size.height);
    if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
        self.snifferData[@"orientation"] = @"portrait";
    } else if (orientation == UIDeviceOrientationLandscapeRight || orientation == UIDeviceOrientationLandscapeLeft) {
        self.snifferData[@"orientation"] = @"landscape";
    } else {
        self.snifferData[@"orientation"] = @"unknow";
    }
    NSMutableArray *arr = [NSMutableArray new];
    self.snifferData[@"views"] = arr;
    
    UIViewController* controller = [self getTopMostViewController];
    if (controller != nil) {
        UIView *view = [controller view];
        [self measureView: view];
    }
}
+ (UIWindow *) getMainWindow
{
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    if (window.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for (windows in windows) {
            if (window.windowLevel == UIWindowLevelNormal) {
                break;
            }
        }
    }
    return window;
}
- (UIViewController *) getTopMostViewController
{
    for (UIView *subView in [mainWindow subviews])
    {
        UIResponder *responder = [subView nextResponder];
        
        //added this block of code for iOS 8 which puts a UITransitionView in between the UIWindow and the UILayoutContainerView
        if ([responder isEqual:mainWindow])
        {
            //this is a UITransitionView
            if ([[subView subviews] count])
            {
                UIView *subSubView = [subView subviews][0]; //this should be the UILayoutContainerView
                responder = [subSubView nextResponder];
            }
        }
        
        if([responder isKindOfClass:[UIViewController class]]) {
            return [self topViewController: (UIViewController *) responder];
        }
    }
    
    return nil;
}

- (UIViewController *) topViewController: (UIViewController *) controller
{
    BOOL isPresenting = NO;
    do {
        // this path is called only on iOS 6+, so -presentedViewController is fine here.
        UIViewController *presented = [controller presentedViewController];
        isPresenting = (presented != nil);
        if(presented != nil) {
            controller = presented;
        }
        
    } while (isPresenting);
    
    return controller;
}

- (void) measureView: (UIView *)view
{
    BOOL hasChildren = YES;
    if (view == nil) {
        NSLog(@"UIView is nil");
        return;
    }
    NSString *className = NSStringFromClass(view.class);
    if (view.hidden) {
        NSLog(@"UIView(%@) is hidden", className);
        return;
    }
    if([view isKindOfClass:[UIButton class]]){
        [self evalUIButton: (UIButton *)view];
        hasChildren = NO;
    } else if ([view isKindOfClass:[UILabel class]]) {
        [self evalUILabel: (UILabel *)view];
        hasChildren = NO;
    }
    if (!hasChildren) {
        return;
    }
    NSArray *views = [view subviews];
    for (UIView *v in views) {
        [self measureView: v];
    }
}

- (void) evalUIButton:(UIButton*)button
{
    CGSize truncated = CGSizeZero;

    if(button.hidden == NO && button.titleLabel.hidden == NO ){
        truncated = [self isUILabelTruncated: button.titleLabel];
        if (CGSizeEqualToSize(truncated, CGSizeZero) == NO) {
            CGRect rect = [button.superview convertRect: button.frame toView: mainWindow];
            TruncationData *t = [[TruncationData alloc ]init: rect];
            t.img = [WindowSniffer takeSnapshot: button];
            t.content = button.titleLabel.text;
            t.className = NSStringFromClass(button.class);
            [self.snifferData[@"views"] addObject: t];
        }
    }
}


- (void) evalUILabel:(UILabel*)label
{
    CGSize truncated = CGSizeZero;
    NSString *t = label.text;
    if(label.hidden == NO && t.length != 0){
        truncated = [self isUILabelTruncated: label];
        if ( CGSizeEqualToSize(truncated, CGSizeZero) == NO ){
            CGRect aaa = label.frame;
            CGRect rect = [label.superview convertRect: label.frame toView: mainWindow];
            TruncationData *t = [[TruncationData alloc ]init: rect];
            t.img = [WindowSniffer takeSnapshot: label];
            t.content = label.text;
            t.className = NSStringFromClass(label.class);
            [self.snifferData[@"views"] addObject: t];
        }
    }
}

- (CGSize) isUILabelTruncated:(UILabel*)label
{
    CGAffineTransform t = ((UIView*)label).transform;
    if (!CGAffineTransformEqualToTransform(t,CGAffineTransformIdentity)){
        return CGSizeZero; // not supported yet [0, -1, 1, 0, 0, 0] (rotate 90) can be supprted.
    }
    CGFloat pointSize = label.font.pointSize;
    CGSize textSize = label.frame.size;
    CGSize frameSize = label.frame.size;
    BOOL f = NO;
    if (label.adjustsFontSizeToFitWidth==YES) {
        textSize = [label.text sizeWithFont:label.font
                                minFontSize:label.minimumFontSize
                             actualFontSize:&pointSize
                                   forWidth:label.frame.size.width
                              lineBreakMode:label.lineBreakMode];
        //NSDictionary *attributes = @{NSFontAttributeName: [UIFont fontWithName:label.font.fontName size:pointSize]};
        //textSize = [label.text sizeWithAttributes:attributes];
        if( (textSize.width > frameSize.width || textSize.height > (frameSize.height+1.0))){
            f=YES;
        }
    }
    else{
        if (label.numberOfLines != 1 ){
            textSize = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size;
            if (textSize.height > label.frame.size.height) {
                f = YES;
            }
        }
        else{
            NSDictionary *attributes = @{ NSFontAttributeName : label.font };
            textSize = [label.text sizeWithAttributes:attributes];
            if( (textSize.width > frameSize.width || textSize.height > (frameSize.height+1.0))){
                f = YES;
            }
        }
    }
    if (f == NO){
        textSize = [self isUILabelTruncatedByScreen:label textSize:textSize];
        if ( CGSizeEqualToSize(textSize,CGSizeZero) == NO ){
            f = YES;
        }
    }
    if (f == YES)
        return textSize;
    return CGSizeZero;
}

- (CGSize) isUILabelTruncatedByScreen:(UILabel*)label textSize:(CGSize)textSize
{
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    CGRect screenbounds = [[UIScreen mainScreen] bounds];
    CGFloat screenwidth  = screenbounds.size.width;
    CGFloat screenheight = screenbounds.size.height;
    CGPoint org = [label convertPoint:CGPointZero toView:[UIApplication sharedApplication].keyWindow];
    
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1){
        if(UIDeviceOrientationIsLandscape(orientation)){
            CGFloat z = org.x;
            org.x = org.y;
            org.y = z;
            z = screenwidth;
            screenwidth = screenheight;
            screenheight = z;
        }
        if(orientation == UIDeviceOrientationPortraitUpsideDown || orientation == UIInterfaceOrientationLandscapeLeft){
            org.x = screenwidth - org.x;
            org.y = screenheight - org.y;
        }
    }
    if (org.x > 0 && org.y > 0 && org.x < screenwidth && (org.y+textSize.height)< screenheight){
        if ((org.x + textSize.width) > screenwidth ){
            return textSize;
        }
    }
    return CGSizeZero;
}
+ (NSString *) takeSnapshot: (UIView *)view
{
    NSString *base64;
    
    //UIWindow *screenWindow = [[UIApplication sharedApplication] keyWindow];
    //UIGraphicsBeginImageContextWithOptions(screenWindow.frame.size , screenWindow.opaque , 0 );
    UIGraphicsBeginImageContext(view.frame.size);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    // convert UIImage to png data
    if (viewImage != nil) {
        NSData *data = UIImagePNGRepresentation(viewImage);
        base64 = [data base64EncodedStringWithOptions: (NSDataBase64EncodingOptions)0];
    } else {
        base64 = [[NSString alloc] init];
    }
    return base64;
}
- (NSString *) reportHtml
{
    return [self report: @"ReportHtml"];
}

- (NSString *) reportJson
{
    return [self report: @"ReportJson"];
}

- (NSString *) report: (NSString *)resourceName
{
    NSUInteger number = [self.snifferData[@"views"] count];
    if (number == 0)
        return nil;
    NSString *screenshot = [WindowSniffer takeSnapshot: mainWindow];
    self.snifferData[@"screenshot"] = screenshot;
    self.snifferData[@"number"] = @(number);
    NSBundle* bundle = [NSBundle bundleWithIdentifier: @"net.citrite.TruncationBundle"];
    // NSString *filePath = [bundle pathForResource: @"ReportTemplate" ofType: @"html"];
    // NSString *text = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil];

    /* https://github.com/groue/GRMustache */
    /* Renders the `resourceName.mustache` resource of bundle whose identifier is defined in Info.plist */
    GRMustacheTemplate *template =  [GRMustacheTemplate templateFromResource: resourceName bundle: bundle error: nil];
    NSString *rendering = [template renderObject: self.snifferData error: NULL];
    return rendering;
}
@end